#####################################################
#
# 模块通用Makefile
# 解决模块内部对外部依赖、模块的产出问题
# 支持多级文件，通用编译
# 编写人：wmh
# 编写时间：2021/08/05
#
#####################################################
MODULE_PATH_TEMP := .
include $(MODULE_PATH_TEMP)/Makefile_var

# 工程配置 开始

# 编译模式： DEBUG  RELEASE
MODE                := RELEASE
# 宿主机: LINUX WINDOWS 
HOST_PLATFORM       := LINUX
# 目标平台位数bits：64、32
TAG_PLATFORM_BITS   := 64

# 交叉编译器前缀
CROSS_COMPILE_PREFIX = 

# 工程配置 结束

$(warning compile mode: [ $(MODE) ])
$(warning host platform: [ $(HOST_PLATFORM) $(TAG_PLATFORM_BITS) ])
# 源文件转换到temp路径
TMP_FILES := $(patsubst $(MODULE_TOP_SRC_DIR)/%,$(MODULE_TMP_DIR)/%, $(SRC_FILES))

# .o对象文件生成
OBJ_FILES := $(TMP_FILES:.c=.o)
OBJ_FILES := $(OBJ_FILES:.cpp=.o)
ifeq ($(MODE), DEBUG)
$(warning libs: $(LIBS))
$(warning objs: $(OBJ_FILES))
endif

# .d依赖文件生成
DEP_FILES := $(OBJ_FILES:.o=.d)

# 输出产物处理
ifeq ($(HOST_PLATFORM), LINUX)
# Linux

LINK_NAME             = lib$(MODULE_NAME).so
SO_NAME               = lib$(MODULE_NAME).so.$(SO_VERSION)
REAL_NAME             = lib$(MODULE_NAME).so.$(LIB_VERSION)

MODULE_SHARED         = $(MODULE_OUT_DIR)/$(LINK_NAME)
MODULE_SHARED_SO      = $(MODULE_OUT_DIR)/$(SO_NAME)
MODULE_SHARED_VERSION = $(MODULE_OUT_DIR)/$(REAL_NAME)
MODULE_STATIC         = $(MODULE_OUT_DIR)/$(MODULE_NAME).a
MODULE_EXE            = $(MODULE_OUT_DIR)/$(MODULE_NAME)

else
# Windows
MODULE_SHARED         = $(MODULE_OUT_DIR)/$(MODULE_NAME).dll
MODULE_STATIC         = $(MODULE_OUT_DIR)/$(MODULE_NAME).lib
MODULE_EXE            = $(MODULE_OUT_DIR)/$(MODULE_NAME).exe

endif


#****************************************************

# 4.编译器、链接器配置
#****************************************************
# 4.1 交叉编译器选项
ifeq ($(HOST_PLATFORM), LINUX)
	# Linux
	AS		= $(CROSS_COMPILE_PREFIX)as
	LD		= $(CROSS_COMPILE_PREFIX)ld
	CC		= $(CROSS_COMPILE_PREFIX)gcc
	CPP		= $(CC) -E
	AR		= $(CROSS_COMPILE_PREFIX)ar
	NM		= $(CROSS_COMPILE_PREFIX)nm
	CXX	    = $(CROSS_COMPILE_PREFIX)g++
	LINK    = $(CROSS_COMPILE_PREFIX)g++
else
	# Windows
	AS		= $(CROSS_COMPILE_PREFIX)as.exe
	LD		= $(CROSS_COMPILE_PREFIX)ld.exe
	CC		= $(CROSS_COMPILE_PREFIX)gcc.exe
	CPP		= $(CC) -E
	AR		= $(CROSS_COMPILE_PREFIX)ar.exe
	NM		= $(CROSS_COMPILE_PREFIX)nm.exe
	CXX	    = $(CROSS_COMPILE_PREFIX)g++.exe
	LINK    = $(CROSS_COMPILE_PREFIX)g++.exe
endif

# 4.2 编译器参数

# 警告，优化
FLAGS := -Wall -Werror


# 指定头文件路径
FLAGS += $(addprefix -I,$(MODULE_INC_DIR))
# 目标平台位数
FLAGS += -m$(TAG_PLATFORM_BITS)
ifeq ($(TAG_TYPE), shared)
# 动态库位置无关
FLAGS += -fPIC -pedantic -Wall -Werror -Wstrict-prototypes -Wwrite-strings -Wshadow -Winit-self -Wcast-align -Wformat=2 -Wmissing-prototypes -Wstrict-overflow=2 -Wcast-qual -Wc++-compat -Wundef -Wswitch-default -Wconversion
endif

ifeq ($(MODE), DEBUG)
# debug
# 生成调试信息；生成.d依赖文件，输出的依赖文件中，不包含标准头文件
FLAGS += -g -MMD

else
# release
FLAGS += -O2 -DNDEBUG

endif

# c
CFLAGS += -std=c99 $(FLAGS)
# cpp
CXXFLAGS += -std=c++2a $(FLAGS)

# 4.3 链接器参数

# 依赖库
# 编译时依赖库路径
LDFLAGS += -m$(TAG_PLATFORM_BITS) -Wl,--copy-dt-needed-entries
LDFLAGS += $(addprefix -L,$(MODULE_LIB_DIR))
LDFLAGS += $(addprefix -l,$(LIBS))

# [可选] 运行时库路径，也可以直接把运行缺失的库放到默认库路径/usr/lib、/lib
#LDFLAGS += -Wl,-rpath=
# [可选] 库的依赖库，加入这个参数-rpath-link， 因为one shared library may require another
#shared_link :=-Wl,-rpath-link=
#LDFLAGS += $(addprefix $(shared_link), $(MODULE_LIB_DIR))

# 库生成参数
ARFLAGS := crs
SHARED_FLAGS := -Wl,-soname=lib$(MODULE_NAME).so.$(SO_VERSION)
#****************************************************


# 其他
#****************************************************
RM := rm -rf
MKDIR := mkdir -p
CP := cp -d
#****************************************************


# 以下无需更改
# 这样写，可以编译多种目标，和目标组合，比如rebuild就是执行clean后再执行all，妙
.PHONY : all exe shared static objs clean distclean rebuild run wmh

all : $(TAG_TYPE)

exe : $(MODULE_EXE) mvcp

shared : $(MODULE_SHARED) mvcp

static : $(MODULE_STATIC) mvcp

objs : $(OBJ_FILES)

clean :
	@$(RM) $(shell find -name "*.o" 2>&1 >/dev/null)
	@$(RM) $(shell find -name "*.d" 2>&1 >/dev/null)
	@$(RM) $(MODULE_TMP_DIR)

distclean : clean
	@$(RM) $(MODULE_OUT_DIR)

rebuild : distclean all 

run :
	$(info +---------------------------------------------+)
	@printf "   run: $(MODULE_EXE)\n"
	@echo "+---------------------------------------------+\n"
	@$(MODULE_EXE)

mvcp:
ifeq ($(MODE), RELEASE)
	$(shell if [ ! -d $(TAG_PATH) ];then mkdir -p $(TAG_PATH); fi)
	$(CP) $(MODULE_OUT_DIR)/* $(TAG_PATH)
endif
	

wmh : all run

# %包含路径和名称
$(MODULE_TMP_DIR)/%.o: $(MODULE_TOP_SRC_DIR)/%.c
	$(shell if [ ! -d $(dir $@) ];then mkdir -p $(dir $@); fi)
	$(CC) -c -o $@ $(CFLAGS) $< 

$(MODULE_TMP_DIR)/%.o: $(MODULE_TOP_SRC_DIR)/%.cpp
	$(shell if [ ! -d $(dir $@) ];then mkdir -p $(dir $@); fi)
	$(CXX) -c -o $@ $(CXXFLAGS) $< 

-include $(DEP_FILES)

# 生成静态库
$(MODULE_STATIC) : $(OBJ_FILES)
	$(shell if [ ! -d $(dir $@) ];then mkdir -p $(dir $@); fi)
	$(AR) $(ARFLAGS) -o $@ $^

# 生成动态库
ifeq ($(HOST_PLATFORM), LINUX)
# Linux
$(MODULE_SHARED_VERSION): $(OBJ_FILES)
	$(shell if [ ! -d $(dir $@) ];then mkdir -p $(dir $@); fi)
	$(CC) -shared $(SHARED_FLAGS) -o $@ $^ $(LDFLAGS)

$(MODULE_SHARED_SO): $(MODULE_SHARED_VERSION)
	ln -sf $(REAL_NAME) $(MODULE_SHARED_SO)

$(MODULE_SHARED): $(MODULE_SHARED_SO)
	ln -sf $(SO_NAME) $(MODULE_SHARED)
else
# Windows
$(MODULE_SHARED): $(OBJ_FILES)
	$(shell if [ ! -d $(dir $@) ];then mkdir -p $(dir $@); fi)
	$(CC) -shared $(SHARED_FLAGS) -o $@ $^ $(LDFLAGS)
endif

# 生成可执行程序
$(MODULE_EXE) : $(OBJ_FILES)
	$(shell if [ ! -d $(dir $@) ];then mkdir -p $(dir $@); fi)
	$(LINK) -o $@ $^ $(LDFLAGS)

